package org.feichao.xdao.util;

import org.apache.commons.lang.StringUtils;
import org.feichao.xdao.XDAO;

import javax.persistence.Column;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;

/**
 * JDBC工具
 *
 * @author steven0lisa
 */
public class JDBCUtil {

	private Connection connection;

	private PreparedStatement prep;

    private ResultSet resultSet;

    private String dataSourceName;

    public JDBCUtil(String dataSourceName) {
        if(XDAO.getDataSource(dataSourceName) == null){
            throw new RuntimeException("Please set data source: XDAO.setDataSource()");
        }
        this.dataSourceName = dataSourceName;
        connection = XDAO.getDataSource(dataSourceName).getConnection();
    }

    public JDBCUtil() {
        if(XDAO.getDataSource() == null){
            throw new RuntimeException("Please set data source: XDAO.setDataSource()");
        }
        connection = XDAO.getDataSource().getConnection();
    }

    public PreparedStatement createPreparedStatement(String sql){
        try {
            prep = this.connection.prepareStatement(sql);
            connection.setAutoCommit(false);
            return prep;
        } catch (SQLException e) {
            close();
            throw new RuntimeException(e);
        }
    }

    /**
     * 执行查询
     *
     * @param sql
     * @param params
     * @return
     */
    public ResultSet executeQuery(String sql, Object[] params){
		try {
			prep = this.connection.prepareStatement(sql);
			setPreparedStatementParams(prep, params);
			resultSet = prep.executeQuery();
			return resultSet;
		} catch (SQLException e1) {
			close();
			throw new RuntimeException(e1);
		}
	}

    /**
     * 执行单条更新
     *
     * @param sql
     * @param params
     * @return
     */
    public int executeUpdate(String sql, Object[] params){
        try {
            prep = this.connection.prepareStatement(sql);
            setPreparedStatementParams(prep, params);
            return prep.executeUpdate();
        } catch (SQLException e1) {
            close();
            throw new RuntimeException(e1);
        }finally {
            try {
                connection.commit();
            } catch (SQLException e) {
                Log.error(e, "Failed to commit!");
            }
        }
    }

    public void setPreparedStatementParams(PreparedStatement prep, Object[] params) throws SQLException {
        for (int i = 1; i <= params.length ; i++) {
            Object param = params[i-1];
            if(param == null){
                prep.setNull(i, Types.VARCHAR);
            }else if(param.getClass().equals(int.class) || param instanceof Integer){
                prep.setInt(i, (Integer) param);
            }else if(param.getClass().equals(int.class) || param instanceof Integer){
                prep.setInt(i, (Integer) param);
            }else if(param.getClass().equals(float.class) || param instanceof Float){
                prep.setFloat(i, (Float) param);
            }else if(param.getClass().equals(long.class) || param instanceof Long){
                prep.setLong(i, (Long) param);
            }else if(param.getClass().equals(double.class) || param instanceof Double){
                prep.setDouble(i, (Double) param);
            }else if(param.getClass().equals(byte.class) || param instanceof Byte){
                prep.setByte(i, (Byte) param);
            }else if(param.getClass().equals(boolean.class) || param instanceof Boolean){
                prep.setBoolean(i, (Boolean) param);
            }else if(param instanceof String){
                prep.setString(i, (String) param);
            }else if(param instanceof Date){
                prep.setDate(i, (Date)param);
            }else if(param instanceof java.util.Date){
                java.util.Date d = (java.util.Date)param;
                prep.setDate(i, new Date(d.getTime()));
            }else{
                Log.warn("Unknown type[%s] in executeQuery", param.getClass().getName());
            }
        }
    }

    /**
     * 关闭资源
     */
	public void close() {
        if(connection!=null){
            try {
                connection.commit();
            } catch (SQLException e) {
                Log.error(e, "Failed to commit connection!");
            }
        }
        XDAO.DataSource ds = null;
        if(StringUtils.isEmpty(dataSourceName)){
            ds = XDAO.getDataSource();
        }else{
            ds = XDAO.getDataSource(dataSourceName);
        }
        closeAll(resultSet, prep);
        if(!ds.close()){
            closeAll(connection);
        }
        resultSet = null;
		prep = null;
		connection = null;
	}

    /**
     * 释放资源（通过反射来调用close方法）
     *
     * @param objects
     */
    public static void closeAll(Object... objects) {
        for (Object o : objects) {
            if (o != null) {
                try {
                    Method m = o.getClass().getMethod("close");
                    m.invoke(o);
                } catch (Exception e) {

                }
            }
        }
    }

    public static Object parseResultSet(ResultSet rs, Class type) throws Exception {
        if(String.class.equals(type)){
            return rs.getString(1);
        }else if(Integer.class.equals(type)){
            return rs.getInt(1);
        }else if(Long.class.equals(type)){
            return rs.getLong(1);
        }else if(Double.class.equals(type)){
            return rs.getDouble(1);
        }else if(Boolean.class.equals(type)){
            return rs.getBoolean(1);
        }else{
            Object data = type.newInstance();
            for(Field field : type.getFields()){
                String name = field.getName();
                if(field.isAnnotationPresent(Column.class)){
                    Column column = field.getAnnotation(Column.class);
                    name = column.name();
                }

                try {
                    FieldAccessUtil.setter(data, field.getName(), rs.getObject(name));
                }catch (Exception e){

                }
            }
            return  data;
        }

    }


}
